package cn.thens.kdroid.core.storage

import android.content.SharedPreferences
import android.util.LruCache
import cn.thens.kdroid.core.io.Codec
import java.io.File

interface Storage<K, V> {
    fun node(key: K): Node<V>
    fun clear()
    fun getAll(): Map<K, V>
    fun getKeys(): Set<K>

    fun cached(cache: Storage<K, V>): Storage<K, V> {
        return CachedStorage(this, cache)
    }

    fun cached(cache: LruCache<K, V>): Storage<K, V> {
        return cached(from(cache))
    }

    fun cached(maxSize: Int): Storage<K, V> {
        return cached(LruCache(maxSize))
    }

    fun cached(map: MutableMap<K, V> = HashMap()): Storage<K, V> {
        return cached(from(map))
    }

    fun <V2> codec(codec: Codec<V, V2>): Storage<K, V2> {
        return CodecStorage(this, codec)
    }

    fun rx(): RxStorage<K, V> {
        return RxStorage(this)
    }

    interface Node<V> {
        fun get(): V
        fun put(value: V)
        fun clear()
        fun exists(): Boolean

        fun optional(): V? {
            return try {
                get()
            } catch (e: Exception) {
                null
            }
        }

        fun cached(cache: Node<V> = node()): Node<V> {
            return CachedStorage.Node(this, cache)
        }

        fun <V2> codec(codec: Codec<V, V2>): Node<V2> {
            return CodecStorage.Node(this, codec)
        }

        fun rx(): RxStorage.Node<V> {
            return RxStorage.Node(this)
        }
    }

    companion object {
        fun from(prefs: SharedPreferences): Storage<String, String> {
            return PreferencesStorage(prefs)
        }

        fun node(prefs: SharedPreferences, key: String): Node<String> {
            return PreferencesStorage.Node(prefs, key)
        }

        fun node(file: File): Node<ByteArray> {
            return FileStorageNode(file)
        }

        fun <V> node(): Node<V> {
            return MemoryStorageNode()
        }

        fun <K, V> from(map: MutableMap<K, V>): Storage<K, V> {
            return MapStorage(map)
        }

        fun <K, V> node(map: MutableMap<K, V>, key: K): Node<V> {
            return MapStorage.Node(map, key)
        }

        fun <K, V> from(cache: LruCache<K, V>): Storage<K, V> {
            return LruCacheStorage(cache)
        }

        fun <K, V> node(cache: LruCache<K, V>, key: K): Node<V> {
            return LruCacheStorage.Node(cache, key)
        }
    }
}

